package com.hzins.scher.register;

import java.lang.reflect.Method;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import com.google.common.base.Preconditions;
import com.hzins.scher.annotation.Scheduled;
import com.hzins.scher.core.JobRegister;
import com.hzins.scher.core.JobTarget;
import com.hzins.scher.core.SchedulerHolder;
import com.hzins.scher.util.ScherUtils;

@Component
public class AnnotationScherJobRegister implements JobRegister,
		BeanPostProcessor, ApplicationContextAware, Ordered {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private SchedulerHolder schedulerHolder;

	private ApplicationContext applicationContext;

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		register(bean, beanName);
		return bean;
	}

	public void register(Object bean, String beanName) {
		Method[] methods = bean.getClass().getDeclaredMethods();

		for (Method md : methods) {
			if (md.isAnnotationPresent(Scheduled.class)) {
				Scheduled scheduled = (Scheduled) md.getAnnotation(Scheduled.class);
				this.schedulerHolder.removeJob(ScherUtils.extractJobName(md),this.schedulerHolder.getSchedulerName());
				JobTarget jobTarget = processJobTarget(beanName, md, scheduled.jobDataRef());
				this.schedulerHolder.createJob(ScherUtils.extractJobName(md),
						this.schedulerHolder.getSchedulerName(),
						scheduled.cron(), scheduled.durability(),
						scheduled.recoverable(), jobTarget);
				this.logger
						.info("############Register a scher of job by annocation,DESC:{}|{}",
								scheduled, md + "#################");
			}
		}
	}

	private JobTarget processJobTarget(String targetBeanName, Method method,
			String jobDataReference) {
		JobTarget jobTarget = null;
		if (StringUtils.isNotBlank(jobDataReference)) {
			Map<String, Object> methodParameters = (Map) this.applicationContext
					.getBean(jobDataReference);
			Class[] types = method.getParameterTypes();
			Preconditions
					.checkArgument((types.length == 1),
							"The targetJob mehtod  parameters can not be greater than 1");
			Preconditions
					.checkArgument(Map.class.equals(types[0]),
							"The targetJob mehtod  parameters must be Map<String,Object>");
			jobTarget = JobTarget.of(targetBeanName, method.getName(),
					methodParameters);
		} else {
			jobTarget = JobTarget.of(targetBeanName, method.getName());
		}
		return jobTarget;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

	public int getOrder() {
		return Integer.MAX_VALUE;
	}
}
